﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using System.Xml;

namespace DbAdapter.sqldbAdapter
{
    public class sqldb
    {
        #region "私有方法和解析"

        //由于此类库只提供静态的方法，因此默认提供私有结构以防被“new SqlData”
        public sqldb()
        { }
        /*
        '附加一个SqlParameters数组到SqlCommand对象中
        '对参数的方向是“InputOutput”和值为"null"赋为"DbNull"
        ' 参数:
        ' -command - SqlCommand对象
        ' -commandParameters -SqlParameters 数组
        '分配参数
        */
        private void AttachParameters(SqlCommand command, SqlParameter[] commandParameters)
        {
            foreach (SqlParameter p in commandParameters)
            {
                //check for derived output value with no value assigned
                if (p.Direction == ParameterDirection.InputOutput & p.Value != null)
                {
                    p.Value = null;
                }
                command.Parameters.Add(p);
            }
        }
        /*
        '给SqlParameters数组赋值.
        ' 参数:
        ' -commandParameters -  SqlParameters数组
        ' -parameterValues()-将要被分配参数值数组
        '分配参数值
        */
        private void AssignParameterValues(SqlParameter[] commandParameters, Object[] parameterValues)
        {
            short i;
            short j;

            if (commandParameters == null & parameterValues == null)
            {
                //如没有任何数据，则直接返回
                return;
            }
            //参数与参数值必须相等，否则返回错误
            if (commandParameters.Length != parameterValues.Length)
            {
                throw new ArgumentException("Parameter count does not match Parameter Value count.");
            }

            //赋值
            j = (short)(commandParameters.Length - 1);
            for (i = 0; i <= j; i++)
            {
                commandParameters[i].Value = parameterValues[i];
            }
        }
        /*
        '给command对象打开连接（如有必要的话）和分配一个连接、事务、类型与参数
        ' 参数:
        ' -command - SqlCommand对象
        ' -connection - 数据库的连接
        ' -transaction -事务, 或 'null'
        ' -commandType - 类型（存储过程、文本等）
        ' -commandText -存储过程名或SQL语句
        ' -commandParameters -参数数据或如没有参数则可为Null
        '预处理command对象
       */
        private void PrepareCommand(SqlCommand command, SqlConnection connection, SqlTransaction transaction,
                                    CommandType commandType, string commandText, SqlParameter[] commandParameters)
        {
            //如提供的连接没有打开，则打开连接
            if (connection.State != ConnectionState.Open)
            {
                connection.Open();
            }

            //使数据库的连接与command发生联系
            command.Connection = connection;

            //设置command的内容
            command.CommandText = commandText;

            //如果有事务对象，则分配事务
            if (transaction != null)
            {
                command.Transaction = transaction;
            }

            //设置类型
            command.CommandType = commandType;

            //如有参数数组，则附加参数
            if (commandParameters != null)
            {
                AttachParameters(command, commandParameters);
            }
        }
        #endregion

        #region "ExecuteNonQuery"
        /*
        '执行无数据返回和没有附加参数的SqlCommand
        '示例：
        '  Dim result as Integer =  ExecuteNonQuery(connString, CommandType.StoredProcedure, "PublishOrders")
        ' 参数:
        ' -connectionString -与数据的连接字符串
        ' -commandType -类型　
        ' -commandText -存储过程名或ＳＱＬ语句 
        ' Returns:返回执行该命令后受影响的行数
        'ExecuteNonQuery
       */
        public int ExecuteNonQuery(string connectionString, CommandType commandType, string commandText)
        {
            return ExecuteNonQuery(connectionString, commandType, commandText, null);
        }
        /*
        '使用提供的参数，执行无数据返回的SqlCommand,
        ' 示例.:  
        ' Dim result as Integer = ExecuteNonQuery(connString, CommandType.StoredProcedure, "PublishOrders", new SqlParameter("@prodid", 24))
        ' 参数:
        ' -connectionString -与数据的连接字符串
        ' -commandType -类型
        ' -commandText -存储过程名或ＳＱＬ语句
        ' -commandParameters -参数数组
        ' Returns: 返回执行该命令后受影响的行数
        'ExecuteNonQuery
       */
        public int ExecuteNonQuery(string connectionString, CommandType commandType, string commandText, SqlParameter[] commandParameters)
        {
            //create & open a SqlConnection, and dispose of it after we are done.
            SqlConnection cn = new SqlConnection(connectionString);
            try
            {
                cn.Open();
                //call the overload that takes a connection in place of the connection string
                return ExecuteNonQuery(cn, commandType, commandText, commandParameters);
            }
            finally
            {
                cn.Dispose();
            }
        }
        /*
        '通过SqlCommand执行一个存储过程,这一方法将为这一存储过程发现参数并赋值
        '这一方法不能用于有＂output＂类型的参数或返回参数值的存储过程.
        ' 示例.:  
        '  Dim result as Integer = ExecuteNonQuery(connString, "PublishOrders", 24, 36)
        ' Parameters:
        ' -connectionString - 与数据的连接字符串
        ' -spName - 存储过程名
        ' -parameterValues -参数数组
        ' Returns: 返回执行该命令后受影响的行数
         'ExecuteNonQuery
       */
        public int ExecuteNonQuery(string connectionString, string spName, object[] parameterValues)
        {
            SqlParameter[] commandParameters;
            //if we receive parameter values, we need to figure out where they go
            if ((parameterValues != null) & (parameterValues.Length > 0))
            {
                //pull the parameters for this stored procedure from the parameter cache (or discover them & populate the cache)
                commandParameters = SqlDataParameterCache.GetSpParameterSet(connectionString, spName);

                //assign the provided values to these parameters based on parameter order
                AssignParameterValues(commandParameters, parameterValues);

                //call the overload that takes an array of SqlParameters
                return ExecuteNonQuery(connectionString, CommandType.StoredProcedure, spName, commandParameters);
                //otherwise we can just call the SP without params
            }
            else
            {
                return ExecuteNonQuery(connectionString, CommandType.StoredProcedure, spName, null);
            }
        }
        /*
         '执行无返回值的存储过程 
         ' 示例.:  
         ' Dim result as Integer = ExecuteNonQuery(conn, CommandType.StoredProcedure, "PublishOrders")
         ' 参数:
         ' -connection - 与数据的连接
         ' -commandType - 类型
         ' -commandText - 存储过程名或ＳＱＬ语句 
         ' Returns:返回执行该命令后受影响的行数
         'ExecuteNonQuery
       */
        public int ExecuteNonQuery(SqlConnection connection, CommandType commandType, string commandText)
        {
            //pass through the call providing null for the set of SqlParameters
            return ExecuteNonQuery(connection, commandType, commandText, null);
        }
        /*
         '执行无返回值的存储过程 
         ' using the provided parameters.
         ' e.g.:  
         '  Dim result as Integer = ExecuteNonQuery(conn, CommandType.StoredProcedure, "PublishOrders", new SqlParameter("@prodid", 24))
         ' Parameters:
         ' -connection - a valid SqlConnection 
         ' -commandType - the CommandType (stored procedure, text, etc.)
         ' -commandText - the stored procedure name or T-SQL command 
         ' -commandParameters - an array of SqlParamters used to execute the command 
         ' Returns: an int representing the number of rows affected by the command 
          'ExecuteNonQuery
      */
        public int ExecuteNonQuery(SqlConnection connection, CommandType commandType, string commandText, SqlParameter[] commandParameters)
        {
            //create a command and prepare it for execution
            SqlCommand cmd = new SqlCommand();
            int retval;
            PrepareCommand(cmd, connection, null, commandType, commandText, commandParameters);

            //finally, execute the command.
            retval = cmd.ExecuteNonQuery();

            //detach the SqlParameters from the command object, so they can be used again
            cmd.Parameters.Clear();
            return retval;
        }
        /*
         ' Execute a stored procedure via a SqlCommand (that returns no resultset) against the specified SqlConnection 
         ' using the provided parameter values.  This method will discover the parameters for the 
         ' stored procedure, and assign the values based on parameter order.
         ' This method provides no access to output parameters or the stored procedure's return value parameter.
         ' e.g.:  
         '  Dim result as integer = ExecuteNonQuery(conn, "PublishOrders", 24, 36)
         ' Parameters:
         ' -connection - a valid SqlConnection
         ' -spName - the name of the stored procedure 
         ' -parameterValues - an array of objects to be assigned as the input values of the stored procedure 
         ' Returns: an int representing the number of rows affected by the command 
         'ExecuteNonQuery
      */
        public int ExecuteNonQuery(SqlConnection connection, string spName, object[] parameterValues)
        {
            SqlParameter[] commandParameters;
            //if we receive parameter values, we need to figure out where they go
            if ((parameterValues != null) & (parameterValues.Length > 0))
            {
                //pull the parameters for this stored procedure from the parameter cache (or discover them & populate the cache)
                commandParameters = SqlDataParameterCache.GetSpParameterSet(connection.ConnectionString, spName);

                //assign the provided values to these parameters based on parameter order
                AssignParameterValues(commandParameters, parameterValues);

                //call the overload that takes an array of SqlParameters
                return ExecuteNonQuery(connection, CommandType.StoredProcedure, spName, commandParameters);
                //otherwise we can just call the SP without params
            }
            else
            {
                return ExecuteNonQuery(connection, CommandType.StoredProcedure, spName);
            }
        }
        /*
         ' Execute a SqlCommand (that returns no resultset and takes no parameters) against the provided SqlTransaction.
         ' e.g.:  
         '  Dim result as Integer = ExecuteNonQuery(trans, CommandType.StoredProcedure, "PublishOrders")
         ' Parameters:
         ' -transaction - a valid SqlTransaction associated with the connection 
         ' -commandType - the CommandType (stored procedure, text, etc.) 
         ' -commandText - the stored procedure name or T-SQL command 
         ' Returns: an int representing the number of rows affected by the command 
         'ExecuteNonQuery
        */
        public int ExecuteNonQuery(SqlTransaction transaction, CommandType commandType, string commandText)
        {
            //pass through the call providing null for the set of SqlParameters
            return ExecuteNonQuery(transaction, commandType, commandText, null);
        }
        /*
         ' Execute a SqlCommand (that returns no resultset) against the specified SqlTransaction
         ' using the provided parameters.
         ' e.g.:  
         ' Dim result as Integer = ExecuteNonQuery(trans, CommandType.StoredProcedure, "GetOrders", new SqlParameter("@prodid", 24))
         ' Parameters:
         ' -transaction - a valid SqlTransaction 
         ' -commandType - the CommandType (stored procedure, text, etc.) 
         ' -commandText - the stored procedure name or T-SQL command 
         ' -commandParameters - an array of SqlParamters used to execute the command 
         ' Returns: an int representing the number of rows affected by the command 
         'ExecuteNonQuery
        */
        public int ExecuteNonQuery(SqlTransaction transaction, CommandType commandType, string commandText, SqlParameter[] commandParameters)
        {
            //create a command and prepare it for execution
            SqlCommand cmd = new SqlCommand();
            int retval;
            PrepareCommand(cmd, transaction.Connection, transaction, commandType, commandText, commandParameters);
            //finally, execute the command.
            retval = cmd.ExecuteNonQuery();
            //detach the SqlParameters from the command object, so they can be used again
            cmd.Parameters.Clear();
            return retval;
        }
        /*    
         ' Execute a stored procedure via a SqlCommand (that returns no resultset) against the specified SqlTransaction 
         ' using the provided parameter values.  This method will discover the parameters for the 
         ' stored procedure, and assign the values based on parameter order.
         ' This method provides no access to output parameters or the stored procedure's return value parameter.
         ' e.g.:  
         ' Dim result As Integer = SqlHelper.ExecuteNonQuery(trans, "PublishOrders", 24, 36)
         ' Parameters:
         ' -transaction - a valid SqlTransaction 
         ' -spName - the name of the stored procedure 
         ' -parameterValues - an array of objects to be assigned as the input values of the stored procedure 
         ' Returns: an int representing the number of rows affected by the command 
         'ExecuteNonQuery
        */
        public int ExecuteNonQuery(SqlTransaction transaction, string spName, object[] parameterValues)
        {
            SqlParameter[] commandParameters;
            //if we receive parameter values, we need to figure out where they go
            if ((parameterValues != null) & (parameterValues.Length > 0))
            {
                //pull the parameters for this stored procedure from the parameter cache (or discover them & populate the cache)
                commandParameters = SqlDataParameterCache.GetSpParameterSet(transaction.Connection.ConnectionString, spName);

                //assign the provided values to these parameters based on parameter order
                AssignParameterValues(commandParameters, parameterValues);

                //call the overload that takes an array of SqlParameters
                return ExecuteNonQuery(transaction, CommandType.StoredProcedure, spName, commandParameters);
                //otherwise we can just call the SP without params
            }
            else
            {
                return ExecuteNonQuery(transaction, CommandType.StoredProcedure, spName);
            }
        }
        #endregion
        #region "ExecuteDataset"
        /*
        ' Execute a SqlCommand (that returns a resultset and takes no parameters) against the database specified in 
        ' the connection string. 
        ' e.g.:  
        ' Dim ds As DataSet = SqlHelper.ExecuteDataset("", commandType.StoredProcedure, "GetOrders")
        ' Parameters:
        ' -connectionString - a valid connection string for a SqlConnection
        ' -commandType - the CommandType (stored procedure, text, etc.)
        ' -commandText - the stored procedure name or T-SQL command
        ' Returns: a dataset containing the resultset generated by the command
      */
        public DataSet ExecuteDataset(string connectionString, CommandType commandType, string commandText)
        {
            //pass through the call providing null for the set of SqlParameters
            return ExecuteDataset(connectionString, commandType, commandText, null);
        } //ExecuteDataset
        /*
          ' Execute a SqlCommand (that returns a resultset) against the database specified in the connection string 
          ' using the provided parameters.
          ' e.g.:  
          ' Dim ds as Dataset = ExecuteDataset(connString, CommandType.StoredProcedure, "GetOrders", new SqlParameter("@prodid", 24))
          ' Parameters:
          ' -connectionString - a valid connection string for a SqlConnection
          ' -commandType - the CommandType (stored procedure, text, etc.)
          ' -commandText - the stored procedure name or T-SQL command
          ' -commandParameters - an array of SqlParamters used to execute the command
          ' Returns: a dataset containing the resultset generated by the command
         */
        public DataSet ExecuteDataset(string connectionString, CommandType commandType, string commandText, SqlParameter[] commandParameters)
        {
            //create & open a SqlConnection, and dispose of it after we are done.
            SqlConnection cn = new SqlConnection(connectionString);
            try
            {
                cn.Open();

                //call the overload that takes a connection in place of the connection string
                return ExecuteDataset(cn, commandType, commandText, commandParameters);
            }
            finally
            {
                cn.Dispose();
            }
        } //ExecuteDataset
        /*
          ' Execute a stored procedure via a SqlCommand (that returns a resultset) against the database specified in 
          ' the connection string using the provided parameter values.  This method will discover the parameters for the 
          ' stored procedure, and assign the values based on parameter order.
          ' This method provides no access to output parameters or the stored procedure's return value parameter.
          ' e.g.:  
          ' Dim ds as Dataset= ExecuteDataset(connString, "GetOrders", 24, 36)
          ' Parameters:
          ' -connectionString - a valid connection string for a SqlConnection
          ' -spName - the name of the stored procedure
          ' -parameterValues - an array of objects to be assigned as the input values of the stored procedure
          ' Returns: a dataset containing the resultset generated by the command
         */
        public DataSet ExecuteDataset(string connectionString, string spName, object[] parameterValues)
        {
            SqlParameter[] commandParameters;
            //if we receive parameter values, we need to figure out where they go
            if ((parameterValues != null) & (parameterValues.Length > 0))
            {
                //pull the parameters for this stored procedure from the parameter cache (or discover them & populate the cache)
                commandParameters = SqlDataParameterCache.GetSpParameterSet(connectionString, spName);

                //assign the provided values to these parameters based on parameter order
                AssignParameterValues(commandParameters, parameterValues);

                //call the overload that takes an array of SqlParameters
                return ExecuteDataset(connectionString, CommandType.StoredProcedure, spName, commandParameters);
                //otherwise we can just call the SP without params
            }
            else
            {
                return ExecuteDataset(connectionString, CommandType.StoredProcedure, spName);
            }
        } //ExecuteDataset
        /*
          ' Execute a SqlCommand (that returns a resultset and takes no parameters) against the provided SqlConnection. 
          ' e.g.:  
          ' Dim ds as Dataset = ExecuteDataset(conn, CommandType.StoredProcedure, "GetOrders")
          ' Parameters:
          ' -connection - a valid SqlConnection
          ' -commandType - the CommandType (stored procedure, text, etc.)
          ' -commandText - the stored procedure name or T-SQL command
          ' Returns: a dataset containing the resultset generated by the command
         */
        public DataSet ExecuteDataset(SqlConnection connection, CommandType commandType, string commandText)
        {
            //pass through the call providing null for the set of SqlParameters
            return ExecuteDataset(connection, commandType, commandText, null);
        } //ExecuteDataset
        /*
          ' Execute a SqlCommand (that returns a resultset) against the specified SqlConnection 
          ' using the provided parameters.
          ' e.g.:  
          ' Dim ds as Dataset = ExecuteDataset(conn, CommandType.StoredProcedure, "GetOrders", new SqlParameter("@prodid", 24))
          ' Parameters:
          ' -connection - a valid SqlConnection
          ' -commandType - the CommandType (stored procedure, text, etc.)
          ' -commandText - the stored procedure name or T-SQL command
          ' -commandParameters - an array of SqlParamters used to execute the command
          ' Returns: a dataset containing the resultset generated by the command
         */
        public DataSet ExecuteDataset(SqlConnection connection, CommandType commandType, string commandText, SqlParameter[] commandParameters)
        {
            //create a command and prepare it for execution
            SqlCommand cmd = new SqlCommand();
            DataSet ds = new DataSet();
            SqlDataAdapter da;

            PrepareCommand(cmd, connection, null, commandType, commandText, commandParameters);
            //create the DataAdapter & DataSet
            da = new SqlDataAdapter(cmd);
            //fill the DataSet using default values for DataTable names, etc.
            da.Fill(ds);
            //detach the SqlParameters from the command object, so they can be used again
            cmd.Parameters.Clear();
            //return the dataset
            return ds;
        } //ExecuteDataset
        /*
          ' Execute a stored procedure via a SqlCommand (that returns a resultset) against the specified SqlConnection 
          ' using the provided parameter values.  This method will discover the parameters for the 
          ' stored procedure, and assign the values based on parameter order.
          ' This method provides no access to output parameters or the stored procedure's return value parameter.
          ' e.g.:  
          ' Dim ds As Dataset = ExecuteDataset(conn, "GetOrders", 24, 36)
          ' Parameters:
          ' -connection - a valid SqlConnection
          ' -spName - the name of the stored procedure
          ' -parameterValues - an array of objects to be assigned as the input values of the stored procedure
          ' Returns: a dataset containing the resultset generated by the command
         */
        public DataSet ExecuteDataset(SqlConnection connection, string spName, object[] parameterValues)
        {
            //Return ExecuteDataset(connection, spName, parameterValues)
            SqlParameter[] commandParameters;

            //if we receive parameter values, we need to figure out where they go
            if ((parameterValues != null) & (parameterValues.Length > 0))
            {
                //pull the parameters for this stored procedure from the parameter cache (or discover them & populate the cache)
                commandParameters = SqlDataParameterCache.GetSpParameterSet(connection.ConnectionString, spName);
                //assign the provided values to these parameters based on parameter order
                AssignParameterValues(commandParameters, parameterValues);
                //call the overload that takes an array of SqlParameters
                return ExecuteDataset(connection, CommandType.StoredProcedure, spName, commandParameters);
            }
            //otherwise we can just call the SP without params
            else
            {
                return ExecuteDataset(connection, CommandType.StoredProcedure, spName);
            }
        } //ExecuteDataset
        /*
          ' Execute a SqlCommand (that returns a resultset and takes no parameters) against the provided SqlTransaction. 
          ' e.g.:  
          ' Dim ds As Dataset = ExecuteDataset(trans, CommandType.StoredProcedure, "GetOrders")
          ' Parameters
          ' -transaction - a valid SqlTransaction
          ' -commandType - the CommandType (stored procedure, text, etc.)
          ' -commandText - the stored procedure name or T-SQL command
          ' Returns: a dataset containing the resultset generated by the command
         */
        public DataSet ExecuteDataset(SqlTransaction transaction, CommandType commandType, string commandText)
        {
            //pass through the call providing null for the set of SqlParameters
            return ExecuteDataset(transaction, commandType, commandText, null);
        } //ExecuteDataset
        /*
          ' Execute a SqlCommand (that returns a resultset) against the specified SqlTransaction
          ' using the provided parameters.
          ' e.g.:  
          ' Dim ds As Dataset = ExecuteDataset(trans, CommandType.StoredProcedure, "GetOrders", new SqlParameter("@prodid", 24))
          ' Parameters
          ' -transaction - a valid SqlTransaction 
          ' -commandType - the CommandType (stored procedure, text, etc.)
          ' -commandText - the stored procedure name or T-SQL command
          ' -commandParameters - an array of SqlParamters used to execute the command
          ' Returns: a dataset containing the resultset generated by the command
         */
        public DataSet ExecuteDataset(SqlTransaction transaction, CommandType commandType, string commandText, SqlParameter[] commandParameters)
        {
            //create a command and prepare it for execution
            SqlCommand cmd = new SqlCommand();
            DataSet ds = new DataSet();
            SqlDataAdapter da;
            PrepareCommand(cmd, transaction.Connection, transaction, commandType, commandText, commandParameters);
            //create the DataAdapter & DataSet
            da = new SqlDataAdapter(cmd);
            //fill the DataSet using default values for DataTable names, etc.
            da.Fill(ds);

            //detach the SqlParameters from the command object, so they can be used again
            cmd.Parameters.Clear();

            //return the dataset
            return ds;
        } //ExecuteDataset
        /*
         ' Execute a stored procedure via a SqlCommand (that returns a resultset) against the specified
         ' SqlTransaction using the provided parameter values.  This method will discover the parameters for the 
         ' stored procedure, and assign the values based on parameter order.
         ' This method provides no access to output parameters or the stored procedure's return value parameter.
         ' e.g.:  
         ' Dim ds As Dataset = ExecuteDataset(trans, "GetOrders", 24, 36)
         ' Parameters:
         ' -transaction - a valid SqlTransaction 
         ' -spName - the name of the stored procedure
         ' -parameterValues - an array of objects to be assigned as the input values of the stored procedure
         ' Returns: a dataset containing the resultset generated by the command
         */
        public DataSet ExecuteDataset(SqlTransaction transaction, string spName, object[] parameterValues)
        {
            SqlParameter[] commandParameters;
            //if we receive parameter values, we need to figure out where they go
            if ((parameterValues != null) & (parameterValues.Length > 0))
            {
                //pull the parameters for this stored procedure from the parameter cache (or discover them & populate the cache)
                commandParameters = SqlDataParameterCache.GetSpParameterSet(transaction.Connection.ConnectionString, spName);

                //assign the provided values to these parameters based on parameter order
                AssignParameterValues(commandParameters, parameterValues);

                //call the overload that takes an array of SqlParameters
                return ExecuteDataset(transaction, CommandType.StoredProcedure, spName, commandParameters);
                //otherwise we can just call the SP without params
            }
            else
            {
                return ExecuteDataset(transaction, CommandType.StoredProcedure, spName);
            }
        } //ExecuteDataset
        #endregion
        #region "ExecuteReader"
        /*
        ' this enum is used to indicate whether the connection was provided by the caller, or created by SqlHelper, so that
        ' we can set the appropriate CommandBehavior when calling ExecuteReader()
         */
        private enum SqlConnectionOwnership
        {
            //Connection is owned and managed by SqlHelper
            Internal,
            //Connection is owned and managed by the caller
            External
        } //SqlConnectionOwnership
        /*
        ' Create and prepare a SqlCommand, and call ExecuteReader with the appropriate CommandBehavior.
        ' If we created and opened the connection, we want the connection to be closed when the DataReader is closed.
        ' If the caller provided the connection, we want to leave it to them to manage.
        ' Parameters:
        ' -connection - a valid SqlConnection, on which to execute this command 
        ' -transaction - a valid SqlTransaction, or 'null' 
        ' -commandType - the CommandType (stored procedure, text, etc.) 
        ' -commandText - the stored procedure name or T-SQL command 
        ' -commandParameters - an array of SqlParameters to be associated with the command or 'null' if no parameters are required 
        ' -connectionOwnership - indicates whether the connection parameter was provided by the caller, or created by SqlHelper 
        ' Returns: SqlDataReader containing the results of the command 
        */
        private SqlDataReader ExecuteReader(SqlConnection connection, SqlTransaction transaction, CommandType commandType,
                                            string commandText, SqlParameter[] commandParameters, SqlConnectionOwnership connectionOwnership)
        {
            //create a command and prepare it for execution
            SqlCommand cmd = new SqlCommand();
            //create a reader
            SqlDataReader dr;
            PrepareCommand(cmd, connection, transaction, commandType, commandText, commandParameters);

            // call ExecuteReader with the appropriate CommandBehavior
            if (connectionOwnership == SqlConnectionOwnership.External)
            {
                dr = cmd.ExecuteReader();
            }
            else
            {
                dr = cmd.ExecuteReader(CommandBehavior.CloseConnection);
            }
            //detach the SqlParameters from the command object, so they can be used again
            cmd.Parameters.Clear();
            return dr;
        } //ExecuteReader
        /*
        ' Execute a SqlCommand (that returns a resultset and takes no parameters) against the database specified in 
        ' the connection string. 
        ' e.g.:  
        ' Dim dr As SqlDataReader = ExecuteReader(connString, CommandType.StoredProcedure, "GetOrders")
        ' Parameters:
        ' -connectionString - a valid connection string for a SqlConnection 
        ' -commandType - the CommandType (stored procedure, text, etc.) 
        ' -commandText - the stored procedure name or T-SQL command 
        ' Returns: a SqlDataReader containing the resultset generated by the command 
        */
        public SqlDataReader ExecuteReader(string connectionString, CommandType commandType, string commandText)
        {
            //pass through the call providing null for the set of SqlParameters
            return ExecuteReader(connectionString, commandType, commandText, null);
        } //ExecuteReader
        /*
        ' Execute a SqlCommand (that returns a resultset) against the database specified in the connection string 
        ' using the provided parameters.
        ' e.g.:  
        ' Dim dr As SqlDataReader = ExecuteReader(connString, CommandType.StoredProcedure, "GetOrders", new SqlParameter("@prodid", 24))
        ' Parameters:
        ' -connectionString - a valid connection string for a SqlConnection 
        ' -commandType - the CommandType (stored procedure, text, etc.) 
        ' -commandText - the stored procedure name or T-SQL command 
        ' -commandParameters - an array of SqlParamters used to execute the command 
        ' Returns: a SqlDataReader containing the resultset generated by the command 
       */
        public SqlDataReader ExecuteReader(string connectionString, CommandType commandType, string commandText, SqlParameter[] commandParameters)
        {
            //create & open a SqlConnection
            SqlConnection cn = new SqlConnection(connectionString);
            cn.Open();

            try
            {
                //call the private overload that takes an internally owned connection in place of the connection string
                return ExecuteReader(cn, null, commandType, commandText, commandParameters, SqlConnectionOwnership.Internal);
            }
            catch
            {
                //if we fail to return the SqlDatReader, we need to close the connection ourselves
                cn.Dispose();
                return null;
            }
        } //ExecuteReader
        /*   
        ' Execute a stored procedure via a SqlCommand (that returns a resultset) against the database specified in 
        ' the connection string using the provided parameter values.  This method will discover the parameters for the 
        ' stored procedure, and assign the values based on parameter order.
        ' This method provides no access to output parameters or the stored procedure's return value parameter.
        ' e.g.:  
        ' Dim dr As SqlDataReader = ExecuteReader(connString, "GetOrders", 24, 36)
        ' Parameters:
        ' -connectionString - a valid connection string for a SqlConnection 
        ' -spName - the name of the stored procedure 
        ' -parameterValues - an array of objects to be assigned as the input values of the stored procedure 
        ' Returns: a SqlDataReader containing the resultset generated by the command 
        */
        public SqlDataReader ExecuteReader(string connectionString, string spName, object[] parameterValues)
        {
            SqlParameter[] commandParameters;
            //if we receive parameter values, we need to figure out where they go
            if ((parameterValues != null) & (parameterValues.Length > 0))
            {
                //pull the parameters for this stored procedure from the parameter cache (or discover them & populate the cache)
                commandParameters = SqlDataParameterCache.GetSpParameterSet(connectionString, spName);

                //assign the provided values to these parameters based on parameter order
                AssignParameterValues(commandParameters, parameterValues);

                //call the overload that takes an array of SqlParameters
                return ExecuteReader(connectionString, CommandType.StoredProcedure, spName, commandParameters);
                //otherwise we can just call the SP without params
            }
            else
            {
                return ExecuteReader(connectionString, CommandType.StoredProcedure, spName);
            }
        } //ExecuteReader
        /*
        ' Execute a SqlCommand (that returns a resultset and takes no parameters) against the provided SqlConnection. 
        ' e.g.:  
        ' Dim dr As SqlDataReader = ExecuteReader(conn, CommandType.StoredProcedure, "GetOrders")
        ' Parameters:
        ' -connection - a valid SqlConnection 
        ' -commandType - the CommandType (stored procedure, text, etc.) 
        ' -commandText - the stored procedure name or T-SQL command 
        ' Returns: a SqlDataReader containing the resultset generated by the command 
       */
        public SqlDataReader ExecuteReader(SqlConnection connection, CommandType commandType, string commandText)
        {
            return ExecuteReader(connection, commandType, commandText, null);
        } //ExecuteReader
        /*
          ' Execute a SqlCommand (that returns a resultset) against the specified SqlConnection 
          ' using the provided parameters.
          ' e.g.:  
          ' Dim dr As SqlDataReader = ExecuteReader(conn, CommandType.StoredProcedure, "GetOrders", new SqlParameter("@prodid", 24))
          ' Parameters:
          ' -connection - a valid SqlConnection 
          ' -commandType - the CommandType (stored procedure, text, etc.) 
          ' -commandText - the stored procedure name or T-SQL command 
          ' -commandParameters - an array of SqlParamters used to execute the command 
          ' Returns: a SqlDataReader containing the resultset generated by the command 
        */
        public SqlDataReader ExecuteReader(SqlConnection connection, CommandType commandType, string commandText, SqlParameter[] commandParameters)
        {
            //pass through the call to private overload using a null transaction value
            return ExecuteReader(connection, null, commandType, commandText, commandParameters, SqlConnectionOwnership.External);
        } //ExecuteReader
        /*
          ' Execute a stored procedure via a SqlCommand (that returns a resultset) against the specified SqlConnection 
          ' using the provided parameter values.  This method will discover the parameters for the 
          ' stored procedure, and assign the values based on parameter order.
          ' This method provides no access to output parameters or the stored procedure's return value parameter.
          ' e.g.:  
          ' Dim dr As SqlDataReader = ExecuteReader(conn, "GetOrders", 24, 36)
          ' Parameters:
          ' -connection - a valid SqlConnection 
          ' -spName - the name of the stored procedure 
          ' -parameterValues - an array of objects to be assigned as the input values of the stored procedure 
          ' Returns: a SqlDataReader containing the resultset generated by the command 
        */
        public SqlDataReader ExecuteReader(SqlConnection connection, string spName, object[] parameterValues)
        {
            //pass through the call using a null transaction value
            //Return ExecuteReader(connection, CType(Nothing, SqlTransaction), spName, parameterValues) 
            SqlParameter[] commandParameters;

            //if we receive parameter values, we need to figure out where they go
            if ((parameterValues != null) & (parameterValues.Length > 0))
            {
                commandParameters = SqlDataParameterCache.GetSpParameterSet(connection.ConnectionString, spName);
                AssignParameterValues(commandParameters, parameterValues);
                return ExecuteReader(connection, CommandType.StoredProcedure, spName, commandParameters);
                //otherwise we can just call the SP without params
            }
            else
            {
                return ExecuteReader(connection, CommandType.StoredProcedure, spName);
            }
        } //ExecuteReader
        /*
          ' Execute a SqlCommand (that returns a resultset and takes no parameters) against the provided SqlTransaction.
          ' e.g.:  
          ' Dim dr As SqlDataReader = ExecuteReader(trans, CommandType.StoredProcedure, "GetOrders")
          ' Parameters:
          ' -transaction - a valid SqlTransaction  
          ' -commandType - the CommandType (stored procedure, text, etc.) 
          ' -commandText - the stored procedure name or T-SQL command 
          ' Returns: a SqlDataReader containing the resultset generated by the command 
        */
        public SqlDataReader ExecuteReader(SqlTransaction transaction, CommandType commandType, string commandText)
        {
            //pass through the call providing null for the set of SqlParameters
            return ExecuteReader(transaction, commandType, commandText, null);
        } //ExecuteReader
        /*
          ' Execute a SqlCommand (that returns a resultset) against the specified SqlTransaction
          ' using the provided parameters.
          ' e.g.:  
          ' Dim dr As SqlDataReader = ExecuteReader(trans, CommandType.StoredProcedure, "GetOrders", new SqlParameter("@prodid", 24))
          ' Parameters:
          ' -transaction - a valid SqlTransaction 
          ' -commandType - the CommandType (stored procedure, text, etc.)
          ' -commandText - the stored procedure name or T-SQL command 
          ' -commandParameters - an array of SqlParamters used to execute the command 
          ' Returns: a SqlDataReader containing the resultset generated by the command 
        */
        public SqlDataReader ExecuteReader(SqlTransaction transaction, CommandType commandType, string commandText,
                                           SqlParameter[] commandParameters)
        {
            //pass through to private overload, indicating that the connection is owned by the caller
            return ExecuteReader(transaction.Connection, transaction, commandType, commandText, commandParameters, SqlConnectionOwnership.External);
        } //ExecuteReader
        /*
          ' Execute a stored procedure via a SqlCommand (that returns a resultset) against the specified SqlTransaction 
          ' using the provided parameter values.  This method will discover the parameters for the 
          ' stored procedure, and assign the values based on parameter order.
          ' This method provides no access to output parameters or the stored procedure's return value parameter.
          ' e.g.:  
          ' Dim dr As SqlDataReader = ExecuteReader(trans, "GetOrders", 24, 36)
          ' Parameters:
          ' -transaction - a valid SqlTransaction 
          ' -spName - the name of the stored procedure 
          ' -parameterValues - an array of objects to be assigned as the input values of the stored procedure
          ' Returns: a SqlDataReader containing the resultset generated by the command
        */
        public SqlDataReader ExecuteReader(SqlTransaction transaction, string spName, object[] parameterValues)
        {
            SqlParameter[] commandParameters;
            //if we receive parameter values, we need to figure out where they go
            if ((parameterValues != null) & (parameterValues.Length > 0))
            {
                commandParameters = SqlDataParameterCache.GetSpParameterSet(transaction.Connection.ConnectionString, spName);
                AssignParameterValues(commandParameters, parameterValues);
                return ExecuteReader(transaction, CommandType.StoredProcedure, spName, commandParameters);
            }
            //otherwise we can just call the SP without params
            else
            {
                return ExecuteReader(transaction, CommandType.StoredProcedure, spName);
            }
        } //ExecuteReader
        #endregion

        #region "ExecuteScalar"
        /*
        ' Execute a SqlCommand (that returns a 1x1 resultset and takes no parameters) against the database specified in 
        ' the connection string. 
        ' e.g.:  
        ' Dim orderCount As Integer = CInt(ExecuteScalar(connString, CommandType.StoredProcedure, "GetOrderCount"))
        ' Parameters:
        ' -connectionString - a valid connection string for a SqlConnection 
        ' -commandType - the CommandType (stored procedure, text, etc.) 
        ' -commandText - the stored procedure name or T-SQL command 
        ' Returns: an object containing the value in the 1x1 resultset generated by the command
        */
        public object ExecuteScalar(string connectionString, CommandType commandType, string commandText)
        {
            //pass through the call providing null for the set of SqlParameters
            return ExecuteScalar(connectionString, commandType, commandText, null);
        } //ExecuteScalar
        /*
        ' Execute a SqlCommand (that returns a 1x1 resultset) against the database specified in the connection string 
        ' using the provided parameters.
        ' e.g.:  
        ' Dim orderCount As Integer = Cint(ExecuteScalar(connString, CommandType.StoredProcedure, "GetOrderCount", new SqlParameter("@prodid", 24)))
        ' Parameters:
        ' -connectionString - a valid connection string for a SqlConnection 
        ' -commandType - the CommandType (stored procedure, text, etc.) 
        ' -commandText - the stored procedure name or T-SQL command 
        ' -commandParameters - an array of SqlParamters used to execute the command 
        ' Returns: an object containing the value in the 1x1 resultset generated by the command 
        */
        public object ExecuteScalar(string connectionString, CommandType commandType, string commandText, SqlParameter[] commandParameters)
        {
            //create & open a SqlConnection, and dispose of it after we are done.
            SqlConnection cn = new SqlConnection(connectionString);
            try
            {
                cn.Open();
                //call the overload that takes a connection in place of the connection string
                return ExecuteScalar(cn, commandType, commandText, commandParameters);
            }
            finally
            {
                cn.Dispose();
            }
        } //ExecuteScalar
        /*
        ' Execute a stored procedure via a SqlCommand (that returns a 1x1 resultset) against the database specified in 
        ' the connection string using the provided parameter values.  This method will discover the parameters for the 
        ' stored procedure, and assign the values based on parameter order.
        ' This method provides no access to output parameters or the stored procedure's return value parameter.
        ' e.g.:  
        ' Dim orderCount As Integer = CInt(ExecuteScalar(connString, "GetOrderCount", 24, 36))
        ' Parameters:
        ' -connectionString - a valid connection string for a SqlConnection 
        ' -spName - the name of the stored procedure 
        ' -parameterValues - an array of objects to be assigned as the input values of the stored procedure 
        ' Returns: an object containing the value in the 1x1 resultset generated by the command 
        */
        public object ExecuteScalar(string connectionString, string spName, object[] parameterValues)
        {
            SqlParameter[] commandParameters;
            //if we receive parameter values, we need to figure out where they go
            if ((parameterValues != null) & (parameterValues.Length > 0))
            {
                //pull the parameters for this stored procedure from the parameter cache (or discover them & populate the cache)
                commandParameters = SqlDataParameterCache.GetSpParameterSet(connectionString, spName);
                //assign the provided values to these parameters based on parameter order
                AssignParameterValues(commandParameters, parameterValues);
                //call the overload that takes an array of SqlParameters
                return ExecuteScalar(connectionString, CommandType.StoredProcedure, spName, commandParameters);
                //otherwise we can just call the SP without params
            }
            else
            {
                return ExecuteScalar(connectionString, CommandType.StoredProcedure, spName);
            }
        } //ExecuteScalar
        /*
        ' Execute a SqlCommand (that returns a 1x1 resultset and takes no parameters) against the provided SqlConnection. 
        ' e.g.:  
        ' Dim orderCount As Integer = CInt(ExecuteScalar(conn, CommandType.StoredProcedure, "GetOrderCount"))
        ' Parameters:
        ' -connection - a valid SqlConnection 
        ' -commandType - the CommandType (stored procedure, text, etc.) 
        ' -commandText - the stored procedure name or T-SQL command 
        ' Returns: an object containing the value in the 1x1 resultset generated by the command 
        */
        public object ExecuteScalar(SqlConnection connection, CommandType commandType, string commandText)
        {
            //pass through the call providing null for the set of SqlParameters
            return ExecuteScalar(connection, commandType, commandText, null);
        } //ExecuteScalar
        /*
        ' Execute a SqlCommand (that returns a 1x1 resultset) against the specified SqlConnection 
        ' using the provided parameters.
        ' e.g.:  
        ' Dim orderCount As Integer = CInt(ExecuteScalar(conn, CommandType.StoredProcedure, "GetOrderCount", new SqlParameter("@prodid", 24)))
        ' Parameters:
        ' -connection - a valid SqlConnection 
        ' -commandType - the CommandType (stored procedure, text, etc.) 
        ' -commandText - the stored procedure name or T-SQL command 
        ' -commandParameters - an array of SqlParamters used to execute the command 
        ' Returns: an object containing the value in the 1x1 resultset generated by the command 
        */
        public object ExecuteScalar(SqlConnection connection, CommandType commandType, string commandText, SqlParameter[] commandParameters)
        {
            //create a command and prepare it for execution
            SqlCommand cmd = new SqlCommand();
            object retval;

            PrepareCommand(cmd, connection, null, commandType, commandText, commandParameters);
            //execute the command & return the results
            retval = cmd.ExecuteScalar();
            //detach the SqlParameters from the command object, so they can be used again
            cmd.Parameters.Clear();
            return retval;
        } //ExecuteScalar
        /*
        ' Execute a stored procedure via a SqlCommand (that returns a 1x1 resultset) against the specified SqlConnection 
        ' using the provided parameter values.  This method will discover the parameters for the 
        ' stored procedure, and assign the values based on parameter order.
        ' This method provides no access to output parameters or the stored procedure's return value parameter.
        ' e.g.:  
        ' Dim orderCount As Integer = CInt(ExecuteScalar(conn, "GetOrderCount", 24, 36))
        ' Parameters:
        ' -connection - a valid SqlConnection 
        ' -spName - the name of the stored procedure 
        ' -parameterValues - an array of objects to be assigned as the input values of the stored procedure 
        ' Returns: an object containing the value in the 1x1 resultset generated by the command 
        */
        public object ExecuteScalar(SqlConnection connection, string spName, object[] parameterValues)
        {
            SqlParameter[] commandParameters;
            //if we receive parameter values, we need to figure out where they go
            if ((parameterValues != null) & (parameterValues.Length > 0))
            {
                //pull the parameters for this stored procedure from the parameter cache (or discover them & populate the cache)
                commandParameters = SqlDataParameterCache.GetSpParameterSet(connection.ConnectionString, spName);

                //assign the provided values to these parameters based on parameter order
                AssignParameterValues(commandParameters, parameterValues);

                //call the overload that takes an array of SqlParameters
                return ExecuteScalar(connection, CommandType.StoredProcedure, spName, commandParameters);
                //otherwise we can just call the SP without params
            }
            else
            {
                return ExecuteScalar(connection, CommandType.StoredProcedure, spName);
            }
        } //ExecuteScalar
        /*
        ' Execute a SqlCommand (that returns a 1x1 resultset and takes no parameters) against the provided SqlTransaction.
        ' e.g.:  
        ' Dim orderCount As Integer  = CInt(ExecuteScalar(trans, CommandType.StoredProcedure, "GetOrderCount"))
        ' Parameters:
        ' -transaction - a valid SqlTransaction 
        ' -commandType - the CommandType (stored procedure, text, etc.) 
        ' -commandText - the stored procedure name or T-SQL command 
        ' Returns: an object containing the value in the 1x1 resultset generated by the command 
        */
        public object ExecuteScalar(SqlTransaction transaction, CommandType commandType, string commandText)
        {
            //pass through the call providing null for the set of SqlParameters
            return ExecuteScalar(transaction, commandType, commandText, null);
        } //ExecuteScalar
        /*
        ' Execute a SqlCommand (that returns a 1x1 resultset) against the specified SqlTransaction
        ' using the provided parameters.
        ' e.g.:  
        ' Dim orderCount As Integer = CInt(ExecuteScalar(trans, CommandType.StoredProcedure, "GetOrderCount", new SqlParameter("@prodid", 24)))
        ' Parameters:
        ' -transaction - a valid SqlTransaction  
        ' -commandType - the CommandType (stored procedure, text, etc.) 
        ' -commandText - the stored procedure name or T-SQL command 
        ' -commandParameters - an array of SqlParamters used to execute the command 
        ' Returns: an object containing the value in the 1x1 resultset generated by the command 
        */
        public object ExecuteScalar(SqlTransaction transaction, CommandType commandType, string commandText, SqlParameter[] commandParameters)
        {
            //create a command and prepare it for execution
            SqlCommand cmd = new SqlCommand();
            object retval;
            PrepareCommand(cmd, transaction.Connection, transaction, commandType, commandText, commandParameters);
            //execute the command & return the results
            retval = cmd.ExecuteScalar();
            //detach the SqlParameters from the command object, so they can be used again
            cmd.Parameters.Clear();
            return retval;
        } //ExecuteScalar
        /*
        ' Execute a stored procedure via a SqlCommand (that returns a 1x1 resultset) against the specified SqlTransaction 
        ' using the provided parameter values.  This method will discover the parameters for the 
        ' stored procedure, and assign the values based on parameter order.
        ' This method provides no access to output parameters or the stored procedure's return value parameter.
        ' e.g.:  
        ' Dim orderCount As Integer = CInt(ExecuteScalar(trans, "GetOrderCount", 24, 36))
        ' Parameters:
        ' -transaction - a valid SqlTransaction 
        ' -spName - the name of the stored procedure 
        ' -parameterValues - an array of objects to be assigned as the input values of the stored procedure 
        ' Returns: an object containing the value in the 1x1 resultset generated by the command 
        */
        public object ExecuteScalar(SqlTransaction transaction, string spName, object[] parameterValues)
        {
            SqlParameter[] commandParameters;
            //if we receive parameter values, we need to figure out where they go
            if ((parameterValues != null) & (parameterValues.Length > 0))
            {
                //pull the parameters for this stored procedure from the parameter cache (or discover them & populate the cache)
                commandParameters = SqlDataParameterCache.GetSpParameterSet(transaction.Connection.ConnectionString, spName);

                //assign the provided values to these parameters based on parameter order
                AssignParameterValues(commandParameters, parameterValues);

                //call the overload that takes an array of SqlParameters
                return ExecuteScalar(transaction, CommandType.StoredProcedure, spName, commandParameters);
                //otherwise we can just call the SP without params
            }
            else
            {
                return ExecuteScalar(transaction, CommandType.StoredProcedure, spName);
            }
        } //ExecuteScalar

        #endregion
        #region "ExecuteXmlReader"
        /*
        ' Execute a SqlCommand (that returns a resultset and takes no parameters) against the provided SqlConnection. 
        ' e.g.:  
        ' Dim r As XmlReader = ExecuteXmlReader(conn, CommandType.StoredProcedure, "GetOrders")
        ' Parameters:
        ' -connection - a valid SqlConnection 
        ' -commandType - the CommandType (stored procedure, text, etc.) 
        ' -commandText - the stored procedure name or T-SQL command using "FOR XML AUTO" 
        ' Returns: an XmlReader containing the resultset generated by the command 
        */
        public XmlReader ExecuteXmlReader(SqlConnection connection, CommandType commandType, string commandText)
        {
            //pass through the call providing null for the set of SqlParameters
            return ExecuteXmlReader(connection, commandType, commandText, null);
        } //ExecuteXmlReader
        /*
        ' Execute a SqlCommand (that returns a resultset) against the specified SqlConnection 
        ' using the provided parameters.
        ' e.g.:  
        ' Dim r As XmlReader = ExecuteXmlReader(conn, CommandType.StoredProcedure, "GetOrders", new SqlParameter("@prodid", 24))
        ' Parameters:
        ' -connection - a valid SqlConnection 
        ' -commandType - the CommandType (stored procedure, text, etc.) 
        ' -commandText - the stored procedure name or T-SQL command using "FOR XML AUTO" 
        ' -commandParameters - an array of SqlParamters used to execute the command 
        ' Returns: an XmlReader containing the resultset generated by the command 
        */
        public XmlReader ExecuteXmlReader(SqlConnection connection, CommandType commandType, string commandText, SqlParameter[] commandParameters)
        {
            /*
            'pass through the call using a null transaction value
            'Return ExecuteXmlReader(connection, CType(Nothing, SqlTransaction), commandType, commandText, commandParameters)
            'create a command and prepare it for execution
            */
            SqlCommand cmd = new SqlCommand();
            XmlReader retval;
            PrepareCommand(cmd, connection, null, commandType, commandText, commandParameters);
            //create the DataAdapter & DataSet
            retval = cmd.ExecuteXmlReader();
            //detach the SqlParameters from the command object, so they can be used again
            cmd.Parameters.Clear();
            return retval;
        } //ExecuteXmlReader
        /*
        ' Execute a stored procedure via a SqlCommand (that returns a resultset) against the specified SqlConnection 
        ' using the provided parameter values.  This method will discover the parameters for the 
        ' stored procedure, and assign the values based on parameter order.
        ' This method provides no access to output parameters or the stored procedure's return value parameter.
        ' e.g.:  
        ' Dim r As XmlReader = ExecuteXmlReader(conn, "GetOrders", 24, 36)
        ' Parameters:
        ' -connection - a valid SqlConnection 
        ' -spName - the name of the stored procedure using "FOR XML AUTO" 
        ' -parameterValues - an array of objects to be assigned as the input values of the stored procedure 
        ' Returns: an XmlReader containing the resultset generated by the command 
        */
        public XmlReader ExecuteXmlReader(SqlConnection connection, string spName, object[] parameterValues)
        {
            SqlParameter[] commandParameters;

            //if we receive parameter values, we need to figure out where they go
            if ((parameterValues != null) & (parameterValues.Length > 0))
            {
                //pull the parameters for this stored procedure from the parameter cache (or discover them & populate the cache)
                commandParameters = SqlDataParameterCache.GetSpParameterSet(connection.ConnectionString, spName);
                //assign the provided values to these parameters based on parameter order
                AssignParameterValues(commandParameters, parameterValues);
                //call the overload that takes an array of SqlParameters
                return ExecuteXmlReader(connection, CommandType.StoredProcedure, spName, commandParameters);
                //otherwise we can just call the SP without params
            }
            else
            {
                return ExecuteXmlReader(connection, CommandType.StoredProcedure, spName);
            }
        } //ExecuteXmlReader

        /*
        ' Execute a SqlCommand (that returns a resultset and takes no parameters) against the provided SqlTransaction
        ' e.g.:  
        ' Dim r As XmlReader = ExecuteXmlReader(trans, CommandType.StoredProcedure, "GetOrders")
        ' Parameters:
        ' -transaction - a valid SqlTransaction
        ' -commandType - the CommandType (stored procedure, text, etc.) 
        ' -commandText - the stored procedure name or T-SQL command using "FOR XML AUTO" 
        ' Returns: an XmlReader containing the resultset generated by the command 
        */
        public XmlReader ExecuteXmlReader(SqlTransaction transaction, CommandType commandType, string commandText)
        {
            //pass through the call providing null for the set of SqlParameters
            return ExecuteXmlReader(transaction, commandType, commandText, null);
        } //ExecuteXmlReader
        /*
        ' Execute a SqlCommand (that returns a resultset) against the specified SqlTransaction
        ' using the provided parameters.
        ' e.g.:  
        ' Dim r As XmlReader = ExecuteXmlReader(trans, CommandType.StoredProcedure, "GetOrders", new SqlParameter("@prodid", 24))
        ' Parameters:
        ' -transaction - a valid SqlTransaction
        ' -commandType - the CommandType (stored procedure, text, etc.) 
        ' -commandText - the stored procedure name or T-SQL command using "FOR XML AUTO" 
        ' -commandParameters - an array of SqlParamters used to execute the command 
        ' Returns: an XmlReader containing the resultset generated by the command
        */
        public XmlReader ExecuteXmlReader(SqlTransaction transaction, CommandType commandType, string commandText, SqlParameter[] commandParameters)
        {
            //create a command and prepare it for execution
            SqlCommand cmd = new SqlCommand();
            XmlReader retval;
            PrepareCommand(cmd, transaction.Connection, transaction, commandType, commandText, commandParameters);
            //create the DataAdapter & DataSet
            retval = cmd.ExecuteXmlReader();
            //detach the SqlParameters from the command object, so they can be used again
            cmd.Parameters.Clear();
            return retval;
        } //ExecuteXmlReader
        /*
               ' Execute a stored procedure via a SqlCommand (that returns a resultset) against the specified SqlTransaction 
               ' using the provided parameter values.  This method will discover the parameters for the 
               ' stored procedure, and assign the values based on parameter order.
               ' This method provides no access to output parameters or the stored procedure's return value parameter.
               ' e.g.:  
               ' Dim r As XmlReader = ExecuteXmlReader(trans, "GetOrders", 24, 36)
               ' Parameters:
               ' -transaction - a valid SqlTransaction
               ' -spName - the name of the stored procedure 
               ' -parameterValues - an array of objects to be assigned as the input values of the stored procedure 
               ' Returns: a dataset containing the resultset generated by the command
               */
        public XmlReader ExecuteXmlReader(SqlTransaction transaction, string spName, object[] parameterValues)
        {
            SqlParameter[] commandParameters;
            //if we receive parameter values, we need to figure out where they go
            if ((parameterValues != null) & (parameterValues.Length > 0))
            {
                //pull the parameters for this stored procedure from the parameter cache (or discover them & populate the cache)
                commandParameters = SqlDataParameterCache.GetSpParameterSet(transaction.Connection.ConnectionString, spName);

                //assign the provided values to these parameters based on parameter order
                AssignParameterValues(commandParameters, parameterValues);

                //call the overload that takes an array of SqlParameters
                return ExecuteXmlReader(transaction, CommandType.StoredProcedure, spName, commandParameters);
                //otherwise we can just call the SP without params
            }
            else
            {
                return ExecuteXmlReader(transaction, CommandType.StoredProcedure, spName);
            }
        } //ExecuteXmlReader

        #endregion

        #region  "执行存储过程"
        public SqlDbType ConvertFromDB(string typename)
        {
            SqlDbType sqldbt;
            switch (typename.Trim())
            {
                case "varchar": sqldbt = SqlDbType.VarChar; break;
                case "datetime": sqldbt = SqlDbType.DateTime; break;
                case "int": sqldbt = SqlDbType.Int; break;
                case "decimal": sqldbt = SqlDbType.Decimal; break;
                case "char": sqldbt = SqlDbType.Char; break;
                case "float": sqldbt = SqlDbType.Float; break;
                case "image": sqldbt = SqlDbType.Image; break;
                case "money": sqldbt = SqlDbType.Money; break;
                case "numeric": sqldbt = SqlDbType.Decimal; break;
                case "smallint": sqldbt = SqlDbType.SmallInt; break;
                case "text": sqldbt = SqlDbType.Text; break;
                case "tinyint": sqldbt = SqlDbType.TinyInt; break;
                case "real": sqldbt = SqlDbType.Real; break;
                case "bit": sqldbt = SqlDbType.Bit; break;
                default: sqldbt = SqlDbType.VarChar; break;
            }
            return sqldbt;
        }

        #region  "返回存储过程的所有参数"
        public ArrayList GetParameters(string connection, string StroePrc)
        {
            try
            {
                string strSql = "GetParamForProc";
                using (SqlConnection SqlConn = new SqlConnection(connection))
                {
                    SqlCommand comm = new SqlCommand(strSql, SqlConn);
                    comm.CommandType = CommandType.StoredProcedure;
                    comm.Parameters.Add("@prcname", SqlDbType.VarChar, 20);
                    comm.Parameters[0].Value = StroePrc.Trim();
                    try
                    {
                        SqlConn.Open();
                    }
                    catch (SqlException)
                    {
                        return null;
                    }
                    ArrayList paraArray = new ArrayList();

                    SqlDataReader sdr = comm.ExecuteReader();
                    while (sdr.Read())
                    {
                        SqlParameter sqlpara = new SqlParameter();
                        sqlpara.ParameterName = sdr[0].ToString();
                        sqlpara.SqlDbType = ConvertFromDB(sdr[3].ToString().Trim());
                        sqlpara.Size = Convert.ToInt32(sdr[2]);
                        paraArray.Add(sqlpara);
                    }
                    sdr.Close();
                    SqlConn.Close();
                    return paraArray;
                }
            }
            catch (Exception)
            {
                return null;
            }
        }
        #endregion

        #region  根据存储过程进行增加或修改记录。当存储过程中，没有@falg参数时使用此函数。
        public void AddNewRows(string connection, string storename, DataSet pds)
        {
            try
            {
                using (SqlConnection SqlConn = new SqlConnection(connection))
                {
                    SqlCommand comm = new SqlCommand(storename, SqlConn);
                    comm.CommandType = CommandType.StoredProcedure;
                    ArrayList paraA = GetParameters(connection, storename);  //"PurchOrder_Insert"
                    for (int nPara = 0; nPara < paraA.Count; nPara++)
                    {
                        comm.Parameters.Add((SqlParameter)paraA[nPara]);
                    }
                    try
                    {
                        SqlConn.Open();
                    }
                    catch (SqlException sqlex)
                    {
                        throw new Exception(sqlex.Message);
                    }
                    for (int nrow = 0; nrow < pds.Tables[0].Rows.Count; nrow++)
                    {
                        for (int nPara = 0; nPara < comm.Parameters.Count; nPara++)
                        {
                            comm.Parameters[nPara].Value = pds.Tables[0].Rows[nrow][nPara];
                        }
                        comm.ExecuteNonQuery();
                    }
                    SqlConn.Close();
                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }
        #endregion

        #region 当存储过程中，有@falg参数作为执行不同语句的标志时使用。
        public void AddNewRows(string connection, string storename, DataSet pds, int onepara)
        {
            try
            {
                using (SqlConnection SqlConn = new SqlConnection(connection))
                {
                    SqlCommand comm = new SqlCommand(storename, SqlConn);
                    comm.CommandType = CommandType.StoredProcedure;
                    ArrayList paraA = GetParameters(connection, storename);
                    for (int nPara = 0; nPara < paraA.Count; nPara++)
                    {
                        comm.Parameters.Add((SqlParameter)paraA[nPara]);
                    }
                    try
                    {
                        SqlConn.Open();
                    }
                    catch (SqlException sqlex)
                    {
                        throw new Exception(sqlex.Message);
                    }
                    comm.Parameters[0].Value = onepara;
                    for (int nrow = 0; nrow < pds.Tables[0].Rows.Count; nrow++)
                    {
                        for (int nPara = 0; nPara < comm.Parameters.Count - 1; nPara++)
                        {
                            comm.Parameters[nPara + 1].Value = pds.Tables[0].Rows[nrow][nPara];
                        }
                        comm.ExecuteNonQuery();
                    }
                    SqlConn.Close();
                }
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }
        }
        #endregion

        #region 通过DataSet执行有返回结果集的存储过程
        public DataSet RunStoreProcedure(string connection, string storename, DataSet parads)
        {
            try
            {
                using (SqlConnection SqlConn = new SqlConnection(connection))
                {
                    SqlCommand comm = new SqlCommand(storename, SqlConn);
                    comm.CommandType = CommandType.StoredProcedure;
                    ArrayList paraA = GetParameters(connection, storename);
                    for (int nPara = 0; nPara < paraA.Count; nPara++)
                    {
                        comm.Parameters.Add((SqlParameter)paraA[nPara]);
                    }
                    SqlConn.Open();
                    for (int nPara = 0; nPara < comm.Parameters.Count; nPara++)
                    {
                        comm.Parameters[nPara].Value = parads.Tables[0].Rows[nPara][0];
                    }
                    comm.ExecuteNonQuery();
                    SqlDataAdapter sda = new SqlDataAdapter(comm);
                    DataSet ds = new DataSet("dataset");
                    sda.Fill(ds);
                    SqlConn.Close();
                    return ds;
                }
            }
            catch (Exception)
            {
                return null;
            }
        }
        #endregion

        #region 通过DataTable执行有返回结果集的存储过程
        public DataSet RunStoreProcedure(string connection, string storename, DataTable parads)
        {
            try
            {
                using (SqlConnection SqlConn = new SqlConnection(connection))
                {
                    SqlCommand comm = new SqlCommand(storename, SqlConn);
                    comm.CommandType = CommandType.StoredProcedure;
                    ArrayList paraA = GetParameters(connection, storename);
                    for (int nPara = 0; nPara < paraA.Count; nPara++)
                    {
                        comm.Parameters.Add((SqlParameter)paraA[nPara]);
                    }
                    SqlConn.Open();
                    for (int nPara = 0; nPara < comm.Parameters.Count; nPara++)
                    {
                        comm.Parameters[nPara].Value = parads.Rows[nPara][0];
                    }
                    comm.ExecuteNonQuery();
                    SqlDataAdapter sda = new SqlDataAdapter(comm);
                    DataSet ds = new DataSet("dataset");
                    sda.Fill(ds);
                    SqlConn.Close();
                    return ds;
                }
            }
            catch (Exception)
            {
                return null;
            }
        }
        #endregion

        #endregion
    }

    public class SqlDataParameterCache
    {
        public SqlDataParameterCache()
        { }
        #region "private methods, variables, and constructors"


        //Since this class provides only static methods, make the default constructor private to prevent 
        //instances from being created with "new SqlHelperParameterCache()".

        private static Hashtable paramCache = Hashtable.Synchronized(new Hashtable());
        /*
             ' resolve at run time the appropriate set of SqlParameters for a stored procedure
             ' Parameters:
             ' - connectionString - a valid connection string for a SqlConnection
             ' - spName - the name of the stored procedure
             ' - includeReturnValueParameter - whether or not to include their return value parameter>
             ' Returns: SqlParameter[]
             'DiscoverSpParameterSet
             */
        private static SqlParameter[] DiscoverSpParameterSet(string connectionString, string spName, bool includeReturnValueParameter,
                                                           object[] parameterValues)
        {
            SqlConnection cn = new SqlConnection(connectionString);
            SqlCommand cmd = new SqlCommand(spName, cn);
            SqlParameter[] discoveredParameters;
            try
            {
                cn.Open();
                cmd.CommandType = CommandType.StoredProcedure;
                SqlCommandBuilder.DeriveParameters(cmd);
                if (!includeReturnValueParameter)
                {
                    cmd.Parameters.RemoveAt(0);
                }
                discoveredParameters = new SqlParameter[cmd.Parameters.Count]; //-1
                cmd.Parameters.CopyTo(discoveredParameters, 0);
            }
            finally
            {
                cmd.Dispose();
                cn.Dispose();
            }
            return discoveredParameters;
        }

        //deep copy of cached SqlParameter array
        //CloneParameters
        private static SqlParameter[] CloneParameters(SqlParameter[] originalParameters)
        {
            int i;
            int j = originalParameters.Length; //- 1  当用数组的长度减1时，会出错，索引超出。
            SqlParameter[] clonedParameters = new SqlParameter[j];

            for (i = 0; i < j; i++)
            {
                clonedParameters[i] = (SqlParameter)(((ICloneable)originalParameters[i]).Clone());
            }

            return clonedParameters;
        }
        #endregion

        #region "caching functions"
        /*
        ' add parameter array to the cache
        ' Parameters
        ' -connectionString - a valid connection string for a SqlConnection 
        ' -commandText - the stored procedure name or T-SQL command 
        ' -commandParameters - an array of SqlParamters to be cached 
        'CacheParameterSet
        */
        public static void CacheParameterSet(string connectionString, string commandText, SqlParameter[] commandParameters)
        {
            string hashKey = connectionString + ":" + commandText;
            paramCache[hashKey] = commandParameters;
        }
        /* 
              ' retrieve a parameter array from the cache
              ' Parameters:
              ' -connectionString - a valid connection string for a SqlConnection 
              ' -commandText - the stored procedure name or T-SQL command 
              ' Returns: an array of SqlParamters 
              'GetCachedParameterSet
            */
        public static SqlParameter[] GetCachedParameterSet(string connectionString, string commandText)
        {
            string hashKey = connectionString + ":" + commandText;
            SqlParameter[] cachedParameters = (SqlParameter[])(paramCache[hashKey]);

            if (cachedParameters != null)
            {
                return null;
            }
            else
            {
                return CloneParameters(cachedParameters);
            }
        }

        #endregion

        #region "Parameter Discovery Functions"
        /*
         ' Retrieves the set of SqlParameters appropriate for the stored procedure
         ' This method will query the database for this information, and then store it in a cache for future requests.
         ' Parameters:
         ' -connectionString - a valid connection string for a SqlConnection 
         ' -spName - the name of the stored procedure 
         ' Returns: an array of SqlParameters
         'GetSpParameterSet
       */
        public static SqlParameter[] GetSpParameterSet(string connectionString, string spName)
        {
            return GetSpParameterSet(connectionString, spName, false);
        }
        /*
             ' Retrieves the set of SqlParameters appropriate for the stored procedure
             ' 
             ' This method will query the database for this information, and then store it in a cache for future requests.
             ' Parameters:
             ' -connectionString - a valid connection string for a SqlConnection
             ' -spName - the name of the stored procedure 
             ' -includeReturnValueParameter - a bool value indicating whether the return value parameter should be included in the results 
             ' Returns: an array of SqlParameters 
             'GetSpParameterSet
             */
        public static SqlParameter[] GetSpParameterSet(string connectionString, string spName, bool includeReturnValueParameter)
        {
            SqlParameter[] cachedParameters = null;
            string hashKey;

            hashKey = connectionString + ":" + spName + (includeReturnValueParameter ? ":include ReturnValue Parameter" : "");

            cachedParameters = (SqlParameter[])paramCache[hashKey];

            if (cachedParameters == null)
            {
                paramCache[hashKey] = DiscoverSpParameterSet(connectionString, spName, includeReturnValueParameter, null);
                cachedParameters = (SqlParameter[])paramCache[hashKey];
            }
            return CloneParameters(cachedParameters);
        }
        #endregion
    }
}
